using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace CollabApp.Domain.Entities.Room;

/// <summary>
/// 房间实体 - 游戏房间的基本信息和配置
/// 继承BaseEntity，支持软删除和时间戳功能
/// </summary>
[Table("rooms")]
public class Room : BaseEntity
{
    /// <summary>
    /// 房间名称 - 显示给玩家的房间标题
    /// </summary>
    [Required]
    [MaxLength(100)]
    [Column("name")]
    public string Name { get; private set; } = string.Empty;

    /// <summary>
    /// 房主用户ID - 外键，指向创建房间的用户
    /// </summary>
    [Required]
    [Column("owner_id")]
    public Guid OwnerId { get; private set; }

    /// <summary>
    /// 最大玩家数 - 房间可容纳的最大玩家数量
    /// </summary>
    [Column("max_players")]
    public int MaxPlayers { get; private set; } = 4;

    /// <summary>
    /// 当前玩家数 - 房间内当前的玩家数量，通过触发器自动更新
    /// </summary>
    [Column("current_players")]
    public int CurrentPlayers { get; private set; } = 0;

    /// <summary>
    /// 房间密码 - 私有房间的进入密码，为空则表示公开房间
    /// </summary>
    [MaxLength(255)]
    [Column("password")]
    public string? Password { get; private set; }

    /// <summary>
    /// 是否私有房间 - 私有房间不会在房间列表中显示
    /// </summary>
    [Column("is_private")]
    public bool IsPrivate { get; private set; } = false;

    /// <summary>
    /// 房间状态 - 等待中、游戏中、已结束
    /// </summary>
    [Column("status")]
    public RoomStatus Status { get; private set; } = RoomStatus.Waiting;

    /// <summary>
    /// 房间设置 - JSON格式存储房间的自定义配置
    /// </summary>
    [Column("settings", TypeName = "json")]
    public string? Settings { get; private set; }

    // ============ 构造函数 ============

    /// <summary>
    /// 无参构造函数 - EF Core 必需
    /// </summary>
    private Room() { }

    /// <summary>
    /// 私有构造函数 - 仅限工厂方法调用
    /// </summary>
    /// <param name="name">房间名称</param>
    /// <param name="ownerId">房主用户ID</param>
    /// <param name="maxPlayers">最大玩家数</param>
    /// <param name="password">房间密码</param>
    /// <param name="isPrivate">是否私有房间</param>
    /// <param name="settings">房间设置</param>
    private Room(string name, Guid ownerId, int maxPlayers = 4, string? password = null, 
                bool isPrivate = false, string? settings = null)
    {
        Name = name;
        OwnerId = ownerId;
        MaxPlayers = maxPlayers;
        CurrentPlayers = 0;
        Password = password;
        IsPrivate = isPrivate;
        Status = RoomStatus.Waiting;
        Settings = settings;
    }

    // ============ 导航属性 ============
    
    /// <summary>
    /// 房主用户实体 - 多对一关系
    /// </summary>
    [ForeignKey("OwnerId")]
    public virtual Auth.User Owner { get; set; } = null!;
    
    /// <summary>
    /// 房间内的玩家列表 - 一对多关系
    /// </summary>
    public virtual ICollection<RoomPlayer> Players { get; set; } = new List<RoomPlayer>();
    
    /// <summary>
    /// 房间聊天消息列表 - 一对多关系
    /// </summary>
    public virtual ICollection<RoomMessage> Messages { get; set; } = new List<RoomMessage>();
    
    /// <summary>
    /// 房间内进行的游戏列表 - 一对多关系
    /// </summary>
    public virtual ICollection<Game.Game> Games { get; set; } = new List<Game.Game>();

    // ============ 工厂方法 ============

    /// <summary>
    /// 创建新房间 - 工厂方法
    /// </summary>
    /// <param name="name">房间名称</param>
    /// <param name="ownerId">房主用户ID</param>
    /// <param name="maxPlayers">最大玩家数</param>
    /// <param name="password">房间密码</param>
    /// <param name="isPrivate">是否私有房间</param>
    /// <param name="settings">房间设置</param>
    /// <returns>新房间实例</returns>
    public static Room CreateRoom(string name, Guid ownerId, int maxPlayers = 4, 
                                  string? password = null, bool isPrivate = false, string? settings = null)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new ArgumentException("房间名称不能为空", nameof(name));
        if (ownerId == Guid.Empty)
            throw new ArgumentException("房主ID不能为空", nameof(ownerId));
        if (maxPlayers < 2 || maxPlayers > 8)
            throw new ArgumentException("最大玩家数必须在2-8之间", nameof(maxPlayers));

        return new Room(name, ownerId, maxPlayers, password, isPrivate, settings);
    }

    // ============ 业务方法 ============

    /// <summary>
    /// 更新房间信息
    /// </summary>
    /// <param name="name">新房间名称</param>
    /// <param name="maxPlayers">新最大玩家数</param>
    /// <param name="password">新密码</param>
    /// <param name="isPrivate">是否私有</param>
    /// <param name="settings">新设置</param>
    public void UpdateRoomInfo(string? name = null, int? maxPlayers = null, 
                               string? password = null, bool? isPrivate = null, string? settings = null)
    {
        if (Status != RoomStatus.Waiting)
            throw new InvalidOperationException("只能在等待状态下修改房间信息");

        if (!string.IsNullOrWhiteSpace(name))
            Name = name;

        if (maxPlayers.HasValue)
        {
            if (maxPlayers.Value < 2 || maxPlayers.Value > 8)
                throw new ArgumentException("最大玩家数必须在2-8之间");
            if (maxPlayers.Value < CurrentPlayers)
                throw new ArgumentException("最大玩家数不能小于当前玩家数");
            MaxPlayers = maxPlayers.Value;
        }

        if (password != null)
            Password = string.IsNullOrWhiteSpace(password) ? null : password;

        if (isPrivate.HasValue)
            IsPrivate = isPrivate.Value;

        if (settings != null)
            Settings = settings;
    }

    /// <summary>
    /// 增加当前玩家数
    /// </summary>
    public void IncrementPlayerCount()
    {
        if (CurrentPlayers >= MaxPlayers)
            throw new InvalidOperationException("房间已满");

        CurrentPlayers++;
    }

    /// <summary>
    /// 减少当前玩家数
    /// </summary>
    public void DecrementPlayerCount()
    {
        if (CurrentPlayers <= 0)
            throw new InvalidOperationException("房间内没有玩家");

        CurrentPlayers--;
    }

    /// <summary>
    /// 开始游戏
    /// </summary>
    public void StartGame()
    {
        if (Status != RoomStatus.Waiting)
            throw new InvalidOperationException("只能在等待状态下开始游戏");
        if (CurrentPlayers < 2)
            throw new InvalidOperationException("至少需要2名玩家才能开始游戏");

        Status = RoomStatus.Playing;
    }

    /// <summary>
    /// 结束游戏
    /// </summary>
    public void FinishGame()
    {
        if (Status != RoomStatus.Playing)
            throw new InvalidOperationException("只能在游戏状态下结束游戏");

        Status = RoomStatus.Finished;
    }

    /// <summary>
    /// 重置房间状态
    /// </summary>
    public void ResetToWaiting()
    {
        Status = RoomStatus.Waiting;
    }

    /// <summary>
    /// 检查密码
    /// </summary>
    /// <param name="password">要验证的密码</param>
    /// <returns>密码是否正确</returns>
    public bool CheckPassword(string? password)
    {
        // 如果房间没有密码，任何输入都视为正确
        if (string.IsNullOrEmpty(Password))
            return true;

        // 如果房间有密码，必须完全匹配
        return Password.Equals(password, StringComparison.Ordinal);
    }

    /// <summary>
    /// 检查房间是否已满
    /// </summary>
    /// <returns>房间是否已满</returns>
    public bool IsFull() => CurrentPlayers >= MaxPlayers;

    /// <summary>
    /// 检查是否可以加入房间
    /// </summary>
    /// <param name="password">密码</param>
    /// <returns>是否可以加入</returns>
    public bool CanJoin(string? password = null)
    {
        return Status == RoomStatus.Waiting && !IsFull() && CheckPassword(password);
    }

    /// <summary>
    /// 转移房主权限
    /// </summary>
    /// <param name="newOwnerId">新房主的用户ID</param>
    /// <exception cref="ArgumentException">新房主ID无效时抛出</exception>
    public void TransferOwnership(Guid newOwnerId)
    {
        if (newOwnerId == Guid.Empty)
            throw new ArgumentException("新房主ID不能为空", nameof(newOwnerId));
        
        if (newOwnerId == OwnerId)
            throw new ArgumentException("新房主不能是当前房主", nameof(newOwnerId));
            
        OwnerId = newOwnerId;
        UpdatedAt = DateTime.UtcNow;
    }
}

/// <summary>
/// 房间状态枚举
/// </summary>
public enum RoomStatus
{
    /// <summary>
    /// 等待中 - 房间已创建，等待玩家加入和准备
    /// </summary>
    Waiting,
    
    /// <summary>
    /// 游戏中 - 房间内正在进行游戏
    /// </summary>
    Playing,
    
    /// <summary>
    /// 已结束 - 游戏已结束，房间即将关闭
    /// </summary>
    Finished
}
